home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 14642 / 14642.xpi / chrome / content / history.js < prev    next >
Text File  |  2009-10-14  |  53KB  |  1,387 lines

  1. /* Copyright 2009, Boomtango.com.  All Rights Reserved. */
  2. /* history.js
  3.  * Responsible for the history ui window
  4.  */
  5. Components.utils.import("resource://boomtango/app.js");
  6. var bthistory = {
  7.     currView: "category",
  8.     currDur: "day",
  9.     currOffset: 0,
  10.     currTime: Date.now(),
  11.     controllers: {},
  12.     _currHash: "",
  13.     
  14.     // XXX: Perhaps these helper funcitons can go into a util class
  15.     hasClass: function(ele,cls) {
  16.         return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
  17.     },
  18.     addClass: function(ele,cls) {
  19.         if (!this.hasClass(ele,cls)) ele.className += " "+cls;
  20.     },
  21.     removeClass: function(ele,cls) {
  22.         if (this.hasClass(ele,cls)) {
  23.             var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
  24.             ele.className=ele.className.replace(reg,' ');
  25.         }
  26.     },
  27.     
  28.     changeDeck: function(index) {
  29.         var deck = document.getElementById("btfilter_deck");
  30.         deck.selectedIndex = index;
  31.     },
  32.     onNavChanged: function(){
  33.         var calnav = document.getElementById("calnav");
  34.         this.currTime = calnav.dateValue.getTime();
  35.         this.loadView(this.currView, this.currDur);
  36.         document.getElementById("calpanel").hidePopup();
  37.     },
  38.     hideBubblePreview: function(){
  39.         this.changeDeck(0);
  40.     },
  41.     showBubblePreview: function(el, id, isTracker){
  42.         this._hoverTimer = null;
  43.         this._hoverID = id;
  44.         this._hoverShowing = true;
  45.         var panel = document.getElementById("bubble");
  46.  
  47.         var dataset;
  48.         if(isTracker){
  49.             dataset = this.storage.queryTrackerByTrackerID(
  50.                 parseInt(id, 10)
  51.             );
  52.         } else {
  53.             dataset = this.storage.queryTrackerByFTSRowId(
  54.                 parseInt(id, 10)
  55.             ); 
  56.         }
  57.         var data = dataset[0];
  58.         var len = dataset.length;
  59.         var previews = [];
  60.         var thumbID = 0;
  61.         for(var x=0; x < len; x++){
  62.             var type = dataset[x].type;
  63.             if(type == "web"){
  64.                 data = dataset[x];
  65.                 thumbID = x;
  66.                 this._previewdata = data;
  67.             }
  68.             if(this.types[type].preview){
  69.                 previews.push(x);
  70.             }
  71.         }
  72.         var title = data.title;
  73.         if(len == 2){
  74.             panel.setAttribute("style",
  75.                 "border-color: " + 
  76.                     bthistory.app.getTrackerColor(dataset[x == 0 ? 1 : 0].type) + ";");
  77.             title = dataset[x == 0 ? 1 : 0].title;
  78.         } else  if (len == 1){
  79.             panel.setAttribute("style",
  80.                 "border-color: " + 
  81.                     bthistory.app.tracker.types['web'].color + ";");
  82.         }
  83.         document.getElementById("bubble_title_label").setAttribute("value",
  84.             title);
  85.         document.getElementById("bubble_url").setAttribute("value",
  86.             data.url);
  87.         document.getElementById("bubble_url").setAttribute("href",
  88.             data.url);
  89.         var start = new Date(data.starttime);
  90.         document.getElementById("bubble_start").setAttribute("value",
  91.             bthistory.datestring(data.starttime));
  92.         document.getElementById("bubble_start").setAttribute("ftsrowid",
  93.             data.ftsrowid);
  94.         document.getElementById("bubble_start").setAttribute("starttime",
  95.             data.starttime);
  96.  
  97.         document.getElementById("bubble_previous").setAttribute("vid",
  98.             data.id);
  99.         document.getElementById("bubble_remove").setAttribute("ftsid",
  100.             parseInt(id, 10));
  101.         if(data.endtime < 0){
  102.             document.getElementById("bubble_spent").setAttribute("value", "");
  103.         } else {
  104.             var timespent = data.endtime - data.starttime;
  105.             document.getElementById("bubble_spent").setAttribute("value",
  106.                 bthistory.duration(timespent));
  107.         }
  108.  
  109.         this.previewData = dataset;
  110.         this.buildingPreview = true;
  111.         var  useThumb = previews.length == 1;
  112.         if(useThumb){
  113.             this.renderPreview(thumbID);
  114.         } else {
  115.             for(var x=0; x < previews.length; x++){
  116.                 if(previews[x] == thumbID){
  117.                     continue;
  118.                 } else {
  119.                     this.renderPreview(previews[x]);
  120.                     break;
  121.                 }
  122.             }
  123.         }
  124.             
  125.         if(previews.length > 2){
  126.             var box = document.getElementById("bubble_previewcontrols");
  127.             while(box.firstChild){
  128.                 box.removeChild(box.firstChild);
  129.             }
  130.             var column = [box, box, box];
  131.             if(previews.length > 6){
  132.                 var hbox = document.createElement('hbox');
  133.                 column = [document.createElement('vbox'),document.createElement('vbox'),document.createElement('vbox')];
  134.                 hbox.appendChild(column[0]);
  135.                 hbox.appendChild(column[1]);
  136.                 hbox.appendChild(column[2]);
  137.                 box.appendChild(hbox);
  138.             }
  139.             var colID = 0;
  140.             for(var x=0; x < previews.length; x++){
  141.                 
  142.                 if(previews[x] != thumbID){
  143.                     var el = document.createElement('radio');
  144.                     el.setAttribute('label', this.types[dataset[previews[x]].type].name);
  145.                     el.setAttribute('value', previews[x]);
  146.                     column[colID].appendChild(el);
  147.                     colID = (colID + 1) % column.length;
  148.                 }
  149.             }
  150.             box.selectedIndex = 0;
  151.             box.setAttribute("hidden", "false");
  152.         } else {
  153.             document.getElementById("bubble_previewcontrols").
  154.                 setAttribute("hidden", "true");
  155.         }
  156.         this.buildingPreview = false;
  157.         document.getElementById("btfilter_deck").selectedIndex = 1;
  158.         document.getElementById("btfilter_deck").setAttribute("hidden", "false");
  159.     },
  160.     doBubbleStart: function(){
  161.         var el = document.getElementById("bubble_start");
  162.         var ftsrowid = parseInt(el.getAttribute("ftsrowid"), 10);
  163.         var starttime = parseInt(el.getAttribute("starttime"), 10);
  164.  
  165.         this.currTime = starttime;
  166.         this.loadView('calendar', 'hour', ftsrowid);
  167.     },
  168.     changePreview: function(){
  169.         var box = document.getElementById("bubble_previewcontrols");
  170.         if(!this.buildingPreview){
  171.             this.renderPreview(parseInt(box.value, 10));
  172.         }
  173.     },
  174.     renderPreview: function(previewID){
  175.         this.app.log("history::renderPreview("+previewID+")");
  176.  
  177.         var data = this.previewData[previewID];
  178.  
  179.         var preview = document.getElementById("bubble_preview");
  180.         while(preview.childNodes.length){
  181.             preview.removeChild(preview.firstChild);
  182.         }
  183.         if(this.tracker.types[data.type] && 
  184.                this.tracker.types[data.type].preview){
  185.                try {
  186.                     this.tracker.types[data.type].preview(document, preview, data);
  187.                } catch(e) { this.app.log("error in preview for type: " + e.message + "(" + data.type + ")");}
  188.         } else {
  189.             this.tracker.types['web'].preview(document, preview, this._previewdata);
  190.         }
  191.     },
  192.     buildHoverMonitor: function(){
  193.         var box = document.getElementById("body");
  194.         var self = this;
  195.         
  196.         box.addEventListener(
  197.             "dblclick",
  198.             function(e){
  199.                 var node = e.target;
  200.                 var id = node.getAttribute ? node.getAttribute("contentID") : null;
  201.                 while(!id){
  202.                     node = node.parentNode;
  203.                     if(node.id == "body" || !node || !node.getAttribute){
  204.                         break;
  205.                     }
  206.                     id = node.getAttribute("contentID")
  207.                 }
  208.                     
  209.                 if(id){
  210.                     var url = self.storage.FTSIDToURL(id);
  211.                     if(url.length){
  212.                         if(e.metaKey){
  213.                             gBrowser.addTab(url);
  214.                         } else {
  215.                             window.location.href = url;
  216.                         }
  217.                     }
  218.                 }
  219.  
  220.             },
  221.             false
  222.         );
  223.         box.addEventListener(
  224.             "click",
  225.             function(e){
  226.                 var node = e.target;
  227.                 var isTracker = false;
  228.                 var id = null;
  229.                 node.blur();
  230.                 
  231.                 // first check for trackerID
  232.                 id = node.getAttribute ? node.getAttribute('trackerID') : null;
  233.                 while(!id){
  234.                     node = node.parentNode;
  235.                     if(!node || node.id == "body" ||  !node.getAttribute){
  236.                         break;
  237.                     }
  238.                     id = node.getAttribute("trackerID")
  239.                 }
  240.                 // if no tracker, look for content id
  241.                 if(!id){
  242.                     node = e.target;
  243.                     id = node.getAttribute ? node.getAttribute("contentID") : null;
  244.                     while(!id){
  245.                         node = node.parentNode;
  246.                         if(!node || node.id == "body" ||  !node.getAttribute){
  247.                             break;
  248.                         }
  249.                         id = node.getAttribute("contentID")
  250.                     }
  251.                 } else {
  252.                     isTracker = true;
  253.                 }
  254.                     
  255.                 if(id){
  256.                      self.showBubblePreview(node, id, isTracker);
  257.                      
  258.                      if (this.lastSelected) {
  259.                         this.lastSelected.className = this.lastSelected.className.replace(/selected/, '');
  260.                      }
  261.                      
  262.                      node.className += " selected";
  263.                      this.lastSelected = node;
  264.                      
  265.                 }
  266.  
  267.             },
  268.             false
  269.         );
  270.         /*
  271.         box.addEventListener(
  272.             "mouseover",
  273.             function(e){
  274.                 var node = e.target;
  275.                 node.blur();
  276.                 var id = node.getAttribute ? node.getAttribute("contentID") : null;
  277.                 while(!id){
  278.                     node = node.parentNode;
  279.                     if(!node || node.id == "body" ||  !node.getAttribute){
  280.                         break;
  281.                     }
  282.                     id = node.getAttribute("contentID")
  283.                 }
  284.                     
  285.                 if(id){
  286.                     var closebox = document.getElementById("closebox." + id);
  287.                     
  288.                     if (closebox) {
  289.                         closebox.style.visibility = "visible";
  290.                     }
  291.                 }
  292.             },
  293.             false
  294.         );
  295.         box.addEventListener(
  296.             "mouseout",
  297.             function(e){
  298.                 var node = e.target;
  299.                 node.blur();
  300.                 var id = node.getAttribute ? node.getAttribute("contentID") : null;
  301.                 while(!id){
  302.                     node = node.parentNode;
  303.                     if(!node || node.id == "body" ||  !node.getAttribute){
  304.                         break;
  305.                     }
  306.                     id = node.getAttribute("contentID")
  307.                 }
  308.                     
  309.                 if(id){
  310.                     var closebox = document.getElementById("closebox." + id);
  311.                     
  312.                     if (closebox) {
  313.                         closebox.style.visibility = "hidden";
  314.                     }
  315.                 }
  316.             },
  317.             false
  318.         );
  319.         */
  320.  
  321.     },
  322.     handleDeleteHistoryItem: function(historyitem, id) {
  323.         if (id) {
  324.             var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  325.                             .getService(Components.interfaces.nsIPromptService);
  326.  
  327.             var check = {value: this.app.confirmDelete};                   // default the checkbox to false
  328.  
  329.             if (check.value) {
  330.                 var result = prompts.confirmCheck(null, this.app.getString("history.deleteprompt.title"), 
  331.                                         this.app.getString("history.deleteprompt.text"),
  332.                                         this.app.getString("history.deleteprompt.ask"), check);
  333.             
  334.             }
  335.             
  336.             // check.value is now true if the box was checked AND OK was pressed, false if
  337.             // the box was cleared AND OK was pressed, and is the default of true if Cancel was pressed.
  338.             this.app.confirmDelete = check.value;
  339.  
  340.             if (!check.value || result) {
  341.                 this.storage.deleteTrackersForFTSRowId(id);
  342.                 document.getElementById("messagebox_message").value ="History Item Removed";
  343.                 document.getElementById("btfilter_deck").selectedIndex = 2;
  344.                 this.updateView();
  345.             }
  346.             //boomtangoApp.debug("DELETE" + historyitem.id + ", " + historyitem.className);
  347.             //historyitem.parentNode.removeChild(historyitem);
  348.             //this.storage.deleteTrackersForFTSRowId(id);
  349.         }
  350.     },
  351.     scrollIntoView: function(container, node, topslop, bottomslop){
  352.         const TOPSLOP = topslop || 8;
  353.         const BOTTOMSLOP = bottomslop || 8;
  354.         const HSLOP = 8;
  355.  
  356.         if(!node){
  357.             return;
  358.         }
  359.         var cdim = { 
  360.             top: container.boxObject.screenY + TOPSLOP,
  361.             bottom: container.boxObject.screenY + container.boxObject.height - BOTTOMSLOP,
  362.             left: container.boxObject.screenX + HSLOP,
  363.             right: container.boxObject.screenX + container.boxObject.width - HSLOP
  364.         };
  365.         var ndim =  { 
  366.             top: node.boxObject.screenY,
  367.             bottom: node.boxObject.height + node.boxObject.screenY,
  368.             left: node.boxObject.screenX,
  369.             right: node.boxObject.screenX + container.boxObject.width
  370.         };
  371.         var scrollAmountX = 0;
  372.         var scrollAmountY = 0;
  373.  
  374.         //dump("cdim: " + cdim.toSource() + "\n");
  375.         //dump("ndim: " + ndim.toSource() + "\n");
  376.         // check vertical
  377.         if(ndim.top >= cdim.top && ndim.bottom  <= cdim.bottom){
  378.             scrollAmountY = 0;
  379.         } else if (ndim.top < cdim.top ){
  380.             scrollAmountY = ndim.top - cdim.top;
  381.         } else {
  382.             scrollAmountY = ndim.bottom - cdim.bottom;
  383.         }
  384.         // check horizontal
  385.         if(ndim.left >= cdim.left && ndim.right  <= cdim.right){
  386.             scrollAmountX = 0;
  387.         } else if (ndim.left < cdim.left ){
  388.             scrollAmountX = ndim.left - cdim.left;
  389.         } else {
  390.             scrollAmountX = ndim.right - cdim.right;
  391.         }
  392.  
  393.         // if we need to scroll, do it
  394.         if(scrollAmountX || scrollAmountY){
  395.             var scroll = container.boxObject.QueryInterface(Components.interfaces.nsIScrollBoxObject);
  396.             scroll.scrollBy(scrollAmountX, scrollAmountY);
  397.         }
  398.     },
  399.     selectNode: function(node){
  400.         var id;
  401.         var isTracker = false;
  402.         
  403.         id = node.getAttribute('trackerID');
  404.         if(id){
  405.             isTracker = true;
  406.         } else {
  407.             id = node.getAttribute('contentID');
  408.         }
  409.         var sels = document.getElementsByClassName('selected');
  410.         var len = sels.length
  411.         for(var x = 0; x < len; x++){
  412.             sels[x].className = sels[x].className.replace(/selected/,'');
  413.         }
  414.         node.className += " selected"; 
  415.         this.showBubblePreview(node, id, isTracker);
  416.     },
  417.     doRemove: function(elid){
  418.         var ftsrowid = parseInt(document.getElementById(elid).getAttribute('ftsid'));
  419.  
  420.         var prompts = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
  421.                         .getService(Components.interfaces.nsIPromptService);
  422.  
  423.         var check = {value: this.app.confirmDelete};                   // default the checkbox to false
  424.  
  425.         if (check.value) {
  426.             var result = prompts.confirmCheck(null, this.app.getString("history.deleteprompt.title"), 
  427.                                     this.app.getString("history.deleteprompt.text"),
  428.                                     this.app.getString("history.deleteprompt.ask"), check);
  429.         
  430.         }
  431.         
  432.         // check.value is now true if the box was checked AND OK was pressed, false if
  433.         // the box was cleared AND OK was pressed, and is the default of true if Cancel was pressed.
  434.         this.app.confirmDelete = check.value;
  435.  
  436.         if (!check.value || result) {
  437.             this.storage.deleteTrackersForFTSRowId(ftsrowid);
  438.             document.getElementById("messagebox_message").value ="History Item Removed";
  439.             document.getElementById("btfilter_deck").selectedIndex = 2;
  440.             this.updateView();
  441.         }
  442.     },
  443.     doVisits: function(elid){
  444.         this.currVisitID = parseInt(document.getElementById(elid).getAttribute('vid'));
  445.         this.resetFilter();
  446.         this.currOrder = 'starttime';
  447.         this.currOrderDesc = true;
  448.         this.currOffset = 0;
  449.         this.currResultType = "visits";
  450.         this.loadView("results", this.currDur);
  451.     },
  452.     doThumbnail: function(){
  453.         this.currOffset = 0;
  454.         this.loadView('thumbnail', bthistory.currDur);
  455.     },
  456.     doSearch: function(){
  457.         var query = document.getElementById("query").value;
  458.         this.resetFilter();
  459.         this.currQuery = query;
  460.         this.currOrder = 'starttime';
  461.         this.currOrderDesc = true;
  462.         this.currOffset = 0;
  463.         this.currResultType = "search";
  464.         this.loadView("results", this.currDur);
  465.     },
  466.     buildSearchBox: function(){
  467.         var tb = document.getElementById("query");
  468.         tb.style.border = "none";
  469.         tb.style.fontSize = "20px";
  470.         tb.style.color = "#333";
  471.     },
  472.     onTypeBox: function(e){
  473.         if(e.target.tagName == "checkbox"){
  474.             document.getElementById("filtertype").selectedIndex = 1;
  475.         }
  476.     },
  477.     buildTypes: function(){
  478.         var box = document.getElementById("typebox");
  479.         var types = box.getElementsByTagName("checkbox");
  480.         var len = types.length;
  481.         var result = [];
  482.  
  483.         if(document.getElementById("filtertype").selectedIndex != 0){
  484.             for(var x = 0; x < len; x++){
  485.                 var item = types[x];
  486.             //
  487.             //if(item.hasAttribute("checked")){
  488.                 if(item.checked){
  489.                     var a = item.id.split('.');
  490.                     result.push(a.slice(1).join('.'));
  491.                 }
  492.             }
  493.         }
  494.         if(!result.length){
  495.             result.push("web");
  496.         }
  497.         return result;
  498.     },
  499.     buildTypeBox: function(){
  500.         var types = this.tracker.types;
  501.         var box = document.getElementById("typebox");
  502.         var self = this;
  503.         box.addEventListener(
  504.             "click",
  505.             function(e){
  506.                 self.onTypeBox(e);
  507.             },
  508.             false
  509.         );
  510.  
  511.        
  512.         this.types = this.tracker.types;
  513.         var columns = [document.createElement('vbox'), document.createElement('vbox')];
  514.  
  515.         while(box.childNodes.length){
  516.             box.removeChild(box.firstChild);
  517.         }
  518.         var colbox = document.createElement('hbox');
  519.         for(var x = 0; x < columns.length; x++){
  520.             colbox.appendChild(columns[x]);
  521.         }
  522.         box.appendChild(colbox);
  523.  
  524.         var curCol = 0;
  525.         for(var x in types){
  526.             if(types.hasOwnProperty(x) && x != "web" && this.app.getTrackerEnabled(x)){
  527.                 var cb = document.createElement("checkbox");
  528.                 cb.setAttribute("label", types[x].name_plural);
  529.                 cb.id = "type." + x;
  530.                 columns[curCol].appendChild(cb);
  531.                 curCol = (curCol + 1) % columns.length;
  532.             }
  533.         }
  534.     },
  535.     doc: document,
  536.     observe: function(subject, topic, data){
  537.         this.app.log("history::observe (" + topic + ")");
  538.         if(topic == "boomtango-visit-change"){
  539.             var ftsrowid = parseInt(data);
  540.  
  541.             var dataset = this.storage.queryTrackerByFTSRowId(ftsrowid);
  542.             this.controllers[this.currView].onHistoryChange(dataset);
  543.         } else if(topic == "boomtango-refreshviews"){
  544.             this.updateView();
  545.         } else if(topic == "boomtango-visit-add"){
  546.             var ftsrowid = parseInt(data);
  547.  
  548.             var dataset = this.storage.queryTrackerByFTSRowId(ftsrowid);
  549.             this.controllers[this.currView].onHistoryAdd(dataset);
  550.         } else if(topic == "boomtango-tracker-remove"){
  551.             var node = this.doc.getElementById("type." + data);
  552.             if(node){
  553.                 node.parentNode.removeChild(node);
  554.             }
  555.             this.updateView();
  556.         } else if(topic == "boomtango-tracker-add"){
  557.             var types = this.types;
  558.             var box = document.getElementById("typebox");
  559.             var columns = box.getElementsByTagName("vbox");
  560.             var minCol = null;
  561.             var len = columns.length;
  562.             while(len--){
  563.                 if(!minCol || minCol.childNodes.length >= columns[len].childNodes.length){
  564.                     minCol = columns[len];
  565.                 }
  566.             }
  567.             var cb = document.createElement("checkbox");
  568.             cb.setAttribute("label", types[data].name_plural);
  569.             cb.id = "type." + data;
  570.             minCol.appendChild(cb);
  571.             this.updateView();
  572.         }
  573.     },
  574.     onUnload: function(){
  575.         var os = Components.classes["@mozilla.org/observer-service;1"].
  576.             getService(Components.interfaces.nsIObserverService);
  577.         os.removeObserver(this, "boomtango-refreshviews");
  578.         os.removeObserver(this, "boomtango-visit-change");
  579.         os.removeObserver(this, "boomtango-visit-add");
  580.         os.removeObserver(this, "boomtango-tracker-add");
  581.         os.removeObserver(this, "boomtango-tracker-remove");
  582.     },
  583.     resetFilter: function(){
  584.         document.getElementById("searchfromcb").checked = false;
  585.         document.getElementById("searchtocb").checked = false;
  586.         document.getElementById("filter_text").value = "";
  587.         document.getElementById("filtertype").selectedIndex = 0;
  588.         var types = this.tracker.types;
  589.         for(var x in types){
  590.             if(types.hasOwnProperty(x) && x != "web"){
  591.                 var cb = document.getElementById("type." + x);
  592.                 if(cb){
  593.                     cb.setAttribute('checked', 'false');
  594.                 }
  595.             }
  596.         }
  597.     },
  598.     handleKeyPress: function(e){
  599.         // let's cache this stuff
  600.         if(!this._keys){
  601.             this._keys = {
  602.                 c: this.app.getString("hotkeys.calendarview").charCodeAt(0),
  603.                 g: this.app.getString("hotkeys.categoryview").charCodeAt(0),
  604.                 s: this.app.getString("hotkeys.summaryview").charCodeAt(0),
  605.                 q: this.app.getString("hotkeys.query").charCodeAt(0),
  606.                 h: this.app.getString("hotkeys.hour").charCodeAt(0),
  607.                 d: this.app.getString("hotkeys.day").charCodeAt(0),
  608.                 w: this.app.getString("hotkeys.week").charCodeAt(0),
  609.                 m: this.app.getString("hotkeys.month").charCodeAt(0),
  610.                 n: this.app.getString("hotkeys.next").charCodeAt(0),
  611.                 p: this.app.getString("hotkeys.previous").charCodeAt(0),
  612.                 t: this.app.getString("hotkeys.today").charCodeAt(0),
  613.                 k: this.app.getString("hotkeys.up").charCodeAt(0),
  614.                 j: this.app.getString("hotkeys.down").charCodeAt(0),
  615.             };
  616.         }
  617.         // check for return key in query
  618.         if(document.activeElement.id == "query" && e.keyCode == 13){
  619.             this.doSearch();
  620.         }
  621.  
  622.         if(document.activeElement.id == "btfilter_text" && e.keyCode == 13){
  623.             this.updateFromFilter();
  624.         }
  625.         if(document.activeElement.id != "query" &&  document.activeElement.id != "btfilter_text"){
  626.             switch(e.keyCode){
  627.                 case 37: // left arrow 
  628.                     this.goPrevious();
  629.                     return;
  630.                 case 39: // right arrow 
  631.                     this.goNext();
  632.                     return;
  633.                 case 38: // up arrow 
  634.                     this.controllers[this.currView].handleUpArrow();
  635.                     return;
  636.                 case 40: // down arrow
  637.                     this.controllers[this.currView].handleDownArrow();
  638.                     return;
  639.             }
  640.             switch(e.charCode){
  641.                 case this._keys['q']: 
  642.                     document.getElementById("query").focus();
  643.                     break;
  644.                 case this._keys['c']: 
  645.                     this.loadView("calendar", this.currDur);
  646.                     break;
  647.                 case this._keys['g']: 
  648.                     this.loadView("category", this.currDur);
  649.                     break;
  650.                 case this._keys['s']: 
  651.                     this.loadView("piechart", this.currDur);
  652.                     break;
  653.                 case this._keys['h']: 
  654.                     this.loadView(this.currView, 'hour');
  655.                     break;
  656.                 case this._keys['d']: 
  657.                     this.loadView(this.currView, 'day');
  658.                     break;
  659.                 case this._keys['w']: 
  660.                     this.loadView(this.currView, 'week');
  661.                     break;
  662.                 case this._keys['m']: 
  663.                     this.loadView(this.currView, 'month');
  664.                     break;
  665.                 case this._keys['n']: 
  666.                     this.goNext();
  667.                     break;
  668.                 case this._keys['p']: 
  669.                     this.goPrevious();
  670.                     break;
  671.                 case this._keys['t']: 
  672.                     this.goToday();
  673.                     break;
  674.                 case this._keys['k']: 
  675.                     this.controllers[this.currView].handleUpArrow();
  676.                     break;
  677.                 case this._keys['j']: 
  678.                     this.controllers[this.currView].handleDownArrow();
  679.                     break;
  680.                     
  681.             }
  682.         }
  683.     },
  684.     resizeToWindow: function(tellControllers){
  685.         var winx = document.getElementById("boomtangoHistory");
  686.         var win = { 
  687.             h: winx.boxObject.height,
  688.             w: winx.boxObject.width,
  689.             x: 0,
  690.             y: 0
  691.         };
  692.         var body = document.getElementById("body");
  693.         var box = { 
  694.             h: body.boxObject.height,
  695.             w: body.boxObject.width,
  696.             x: body.boxObject.x,
  697.             y: body.boxObject.y
  698.         };
  699.  
  700.         this.app.debug("win: " + win.toSource() + "\n");
  701.         this.app.debug("box: " + box.toSource() + "\n");
  702.         var height = Math.max(win.h - box.y, 40);
  703.         this.app.debug("h: " + height + "\n");
  704.         body.setAttribute('height', height);
  705.         if(tellControllers){
  706.             this.controllers[this.currView].handleResize();
  707.         }
  708.     },
  709.     goHome: function(){
  710.         // move it to the start of an hour
  711.         var now = new Date();
  712.         var startOfHour = new Date(
  713.             now.getFullYear(), 
  714.             now.getMonth(), 
  715.             now.getDate(), 
  716.             now.getHours()
  717.         );
  718.         this.currTime = startOfHour.getTime();
  719.         this.loadView('category', 'hour' );
  720.     },
  721.  
  722.     onLoad: function(){
  723.         this.app = boomtangoApp;
  724.         this.app.log("history::load");
  725.         this.storage = this.app.storage;
  726.         this.tracker = this.app.tracker;
  727.         this.currView = this.app.historyView;
  728.         this.currDur = this.app.historyDur;
  729.  
  730.         if(!this.app.showdonate){
  731.             document.getElementById("donate").setAttribute('hidden', 'true');
  732.         }
  733.  
  734.         
  735.         this.buildSearchBox();
  736.         this.buildTypeBox();
  737.         this.buildHoverMonitor();
  738.         var self = this;
  739.         window.addEventListener(
  740.             "keypress",
  741.             function(e){
  742.                 self.handleKeyPress(e);
  743.             },
  744.             true
  745.         );
  746.         window.addEventListener(
  747.             "resize",
  748.             function(e){
  749.                 self.resizeToWindow(true);
  750.             },
  751.             false
  752.         );
  753.         document.getElementById("query").focus();
  754.  
  755.         this._boxwidth = document.getElementById("body").boxObject.width;
  756.         this._filterwidth = document.getElementById("btfilter").boxObject.width;
  757.  
  758.         this.resizeToWindow(false);
  759.         // move it to the start of an hour
  760.         var now = new Date(this.currTime);
  761.         var startOfHour = new Date(
  762.             now.getFullYear(), 
  763.             now.getMonth(), 
  764.             now.getDate(), 
  765.             now.getHours()
  766.         );
  767.  
  768.         var os = Components.classes["@mozilla.org/observer-service;1"].
  769.             getService(Components.interfaces.nsIObserverService);
  770.         os.addObserver(this, "boomtango-refreshviews", false);
  771.         os.addObserver(this, "boomtango-visit-change", false);
  772.         os.addObserver(this, "boomtango-visit-add", false);
  773.         os.addObserver(this, "boomtango-tracker-add", false);
  774.         os.addObserver(this, "boomtango-tracker-remove", false);
  775.  
  776.         this.updateView();
  777.  
  778.         this.monitorHash();
  779.     },
  780.     monitorHash: function(){
  781.         var self = this;
  782.         window.setInterval(
  783.             function(){
  784.                 var orighash = window.location.hash.substring(1);
  785.                 var hash = orighash.replace(/\ /g, '%20').replace(/\+/g, '%2B');
  786.                 if(self._currHash != hash){
  787.                     self.updateView();
  788.                 }
  789.             },
  790.             500
  791.         );
  792.     },
  793.     updateFromFilter: function(){
  794.         this.app.log("history::updateFromFilter");
  795.         this.loadView(this.currView, this.currDur);
  796.     },
  797.     updateView: function(){
  798.         var hash = window.location.hash.substring(1);
  799.         this.app.log("history::updateView");
  800.         this.app.debug("  hash=" + hash);
  801.         if(hash.length){
  802.             var a = hash.split('&');
  803.             var obj = {};
  804.             var len = a.length;
  805.             for(var x = 0; x < len; x++){
  806.                 var i = a[x].split('=');
  807.                 obj[i[0]] = i[1];
  808.             }
  809.  
  810.             if(obj.hasOwnProperty('view')){
  811.                 this.currView = obj['view'];
  812.             }
  813.             if(obj.hasOwnProperty('dur')){
  814.                 this.currDur = obj['dur'];
  815.             }
  816.             if(obj.hasOwnProperty('time')){
  817.                 this.currTime = parseInt(obj['time']);
  818.             }
  819.             if(obj.hasOwnProperty('starttime')){
  820.                 document.getElementById('searchfromcb').checked = true;
  821.                 document.getElementById("searchfrom").dateValue = new Date(parseInt(obj['starttime']));
  822.             }
  823.             if(obj.hasOwnProperty('endtime')){
  824.                 document.getElementById('searchtocb').checked = true;
  825.                 document.getElementById("searchto").dateValue = new Date(parseInt(obj['endtime']));
  826.             }
  827.             if(obj.hasOwnProperty('type')){
  828.                 this.currResultType = obj['type'];
  829.             }
  830.             if(obj.hasOwnProperty('mtype')){
  831.                 this.currMoreType = obj['mtype'];
  832.             }
  833.             if(obj.hasOwnProperty('vid')){
  834.                 this.currVisitID = obj['vid'];
  835.             }
  836.             if(obj.hasOwnProperty('order')){
  837.                 this.currOrder = obj['order'];
  838.             }
  839.             if(obj.hasOwnProperty('desc')){
  840.                 this.currOrderDesc = parseInt(obj['desc']) != 0;
  841.             }
  842.             if(obj.hasOwnProperty('offset')){
  843.                 this.currOffset = parseInt(obj['offset']);
  844.             } else {
  845.                 this.currOffset = 0;
  846.             }
  847.             if(obj.hasOwnProperty('query')){
  848.                 this.currQuery = decodeURIComponent(obj['query']);
  849.                 document.getElementById("query").value = this.currQuery;
  850.             }
  851.             if(obj.hasOwnProperty('filter')){
  852.                 document.getElementById("filter_text").setAttribute("value",
  853.                     decodeURIComponent(obj['filter']));
  854.             }
  855.             if(obj.hasOwnProperty('types')){
  856.                 var a = obj['types'].split(';');
  857.                 var types = this.app.tracker.types;
  858.                 var hasCheck = false;
  859.                 for(var x in types){
  860.                     if(types.hasOwnProperty(x) && x != "web" && this.app.getTrackerEnabled(x)){
  861.                         var cb = document.getElementById("type." + x);
  862.                         if(cb){
  863.                             cb.checked = false;
  864.                         }
  865.                     }
  866.                 }
  867.                 for(var x = 0; x < a.length; x++){
  868.                     if(types.hasOwnProperty(a[x]) && a[x] != "web"){
  869.                         var cb = document.getElementById("type." + a[x]);
  870.                         if(cb){
  871.                             hasCheck = true;
  872.                             cb.checked = true;
  873.                         }
  874.                     }
  875.                 }
  876.                 document.getElementById("filtertype").selectedIndex = hasCheck ? 1 : 0;
  877.             }
  878.         }
  879.  
  880.         this._currHash = hash;
  881.         this.loadView(this.currView, this.currDur);
  882.     },
  883.     ONEMINUTE: 60 * 1000,
  884.     ONEHOUR:  60 * 60 * 1000,
  885.     ONEDAY:   24 * 60 * 60 * 1000,
  886.     ONEWEEK:  7 * 24 * 60 * 60 * 1000,
  887.     _dayLabel: function(currTime){
  888.         var now = Date.now();
  889.         if(currTime < now){
  890.             //check for today
  891.             var startOfDay = this._durHelper['day'].getStart(now);
  892.             if(currTime >= startOfDay){
  893.                return bthistory.app.getString("date.today");
  894.             }
  895.  
  896.             var startOfYesterday = this._durHelper['day'].getStart(
  897.                     now - this.ONEDAY);
  898.             if(currTime >= startOfYesterday){
  899.                 return bthistory.app.getString("date.yesterday");
  900.             }
  901.             var startOfWeek = this._durHelper['week'].getStart(now);
  902.             if(currTime >= startOfWeek){
  903.                 var d = new Date(currTime);
  904.                 return bthistory.app.getString("date.day." + d.getDay());
  905.             }
  906.             startOfWeek = this._durHelper['week'].getStart(now - this.ONEWEEK);
  907.             if(currTime >= startOfWeek){
  908.                 var d = new Date(currTime);
  909.                 return bthistory.app.getString("date.lastday." + d.getDay());
  910.             }
  911.         } else {
  912.             //check for today
  913.             var endOfDay = this._durHelper['day'].getStart(now + this.ONEDAY);
  914.             if(currTime < endOfDay){
  915.                return bthistory.app.getString("date.today");
  916.             }
  917.             //check for tomorrow
  918.             var endOfTomorrow = this._durHelper['day'].getStart(
  919.                     now + (this.ONEDAY * 2));
  920.             if(currTime < endOfTomorrow ){
  921.                 return bthistory.app.getString("date.tomorrow");
  922.             }
  923.             var endOfWeek = this._durHelper['week'].getStart(now + this.ONEWEEK);
  924.             if(currTime < endOfWeek){
  925.                 var d = new Date(currTime);
  926.                 return bthistory.app.getString("date.day." + d.getDay());
  927.             }
  928.             endOfWeek = this._durHelper['week'].getStart(now + (this.ONEWEEK * 2));
  929.             if(currTime < endOfWeek){
  930.                 var d = new Date(currTime);
  931.                 return bthistory.app.getString("date.nextday." + d.getDay());
  932.             }
  933.         }
  934.         var d = new Date(currTime);
  935.         return d.toLocaleDateString();
  936.      },
  937.     _durHelper: {
  938.         hour: {
  939.             nextStart: function(currTime){
  940.                 return currTime + bthistory.ONEHOUR;
  941.             },
  942.             prevStart: function(currTime){
  943.                 return currTime - bthistory.ONEHOUR;
  944.             },
  945.             getRangeLabel: function(currTime){
  946.                 var d = new Date(currTime);
  947.                 return d.toLocaleDateString();
  948.             },
  949.             getLabel: function(currTime){
  950.                 var label = bthistory._dayLabel(currTime);
  951.                 var now = new Date(currTime);
  952.  
  953.                 var d = new Date(this.getStart(currTime));
  954.                 if(now.toLocaleDateString() == label){
  955.                     return d.toLocaleFormat(
  956.                         boomtangoApp.getString("hour.format")
  957.                     );
  958.                 } else {
  959.                     return label + ", " + d.toLocaleFormat(
  960.                             boomtangoApp.getString("hour.format"));
  961.                 }
  962.             },
  963.             getStart: function(currTime){
  964.                 var now = new Date(currTime);
  965.                 var startOfDay = new Date(
  966.                     now.getFullYear(),
  967.                     now.getMonth(),
  968.                     now.getDate(),
  969.                     now.getHours(),
  970.                     0
  971.                 );
  972.                 return startOfDay.getTime();
  973.             },
  974.             getRange: function(currTime){
  975.                 var now = new Date(currTime);
  976.                 var startTime = new Date(
  977.                     now.getFullYear(),
  978.                     now.getMonth(),
  979.                     now.getDate(),
  980.                     now.getHours(),
  981.                     0
  982.                 );
  983.                 return {
  984.                     start: startTime.getTime(),
  985.                     end: startTime.getTime() + bthistory.ONEHOUR 
  986.                 };
  987.             }
  988.         },
  989.         day: {
  990.             nextStart: function(currTime){
  991.                 return currTime + bthistory.ONEDAY;
  992.             },
  993.             prevStart: function(currTime){
  994.                 return currTime - bthistory.ONEDAY;
  995.             },
  996.             getRangeLabel: function(currTime){
  997.                 var d = new Date(currTime);
  998.                 return d.toLocaleDateString();
  999.             },
  1000.             getLabel: function(currTime){
  1001.                 return bthistory._dayLabel(currTime);
  1002.             },
  1003.             getStart: function(currTime){
  1004.                 var now = new Date(currTime);
  1005.                 var startOfDay = new Date(
  1006.                     now.getFullYear(),
  1007.                     now.getMonth(),
  1008.                     now.getDate()
  1009.                 );
  1010.                 return startOfDay.getTime();
  1011.             },
  1012.             getRange: function(currTime){
  1013.                 var start = this.getStart(currTime);
  1014.                 return {
  1015.                     start: start,
  1016.                     end: start + bthistory.ONEDAY
  1017.                 };
  1018.             }
  1019.         },
  1020.         week: {
  1021.             nextStart: function(currTime){
  1022.                 return currTime + bthistory.ONEWEEK;
  1023.             },
  1024.             prevStart: function(currTime){
  1025.                 return currTime - bthistory.ONEWEEK;
  1026.             },
  1027.             getRangeLabel: function(currTime){
  1028.                 // cheat so we don't recalculate this
  1029.                 if(bthistory._rangetime == currTime){
  1030.                     var range = bthistory._range;
  1031.                 } else {
  1032.                     var range = this.getRange(currTime);
  1033.                 }
  1034.                 var start = new Date(range.start);
  1035.                 var end = new Date(range.end - 1);
  1036.  
  1037.                 return start.toLocaleDateString() + " - " +
  1038.                         end.toLocaleDateString();
  1039.             },
  1040.             getLabel: function(currTime){
  1041.                 var now = new Date();
  1042.                 if(now > currTime){
  1043.                     var start = this.getStart(now);
  1044.                     if(currTime >= start){
  1045.                         return bthistory.app.getString("date.thisweek");
  1046.                     }
  1047.                     start = this.getStart(now - bthistory.ONEWEEK);
  1048.                     if(currTime >= start){
  1049.                         return bthistory.app.getString("date.lastweek");
  1050.                     }
  1051.                 } else {
  1052.                     var end = this.getStart(now + bthistory.ONEWEEK);
  1053.                     if(currTime < end){
  1054.                         return bthistory.app.getString("date.thisweek");
  1055.                     }
  1056.                     end = this.getStart(now + (bthistory.ONEWEEK * 2));
  1057.                     if(currTime < end){
  1058.                         return bthistory.app.getString("date.nextweek");
  1059.                     }
  1060.                 }
  1061.                 var start = this.getStart(currTime);
  1062.                 var d = new Date(start);
  1063.                 return bthistory.app.getString("date.weekstarting") + " " + 
  1064.                     d.toLocaleDateString();
  1065.             },
  1066.             getStart: function(currTime){
  1067.                 var now = new Date(currTime);
  1068.                 var startOfDay = new Date(
  1069.                     now.getFullYear(),
  1070.                     now.getMonth(),
  1071.                     now.getDate()
  1072.                 );
  1073.                 return startOfDay.getTime() - 
  1074.                     (startOfDay.getDay() * bthistory.ONEDAY);
  1075.             },
  1076.             getRange: function(currTime){
  1077.                 var start = this.getStart(currTime);
  1078.                 return {
  1079.                     start: start,
  1080.                     end: start+ bthistory.ONEWEEK
  1081.                 };
  1082.             }
  1083.         },
  1084.         month: {
  1085.             nextStart: function(currTime){
  1086.                 var d = new Date(currTime);
  1087.                 if(d.getMonth == 11){
  1088.                     var next = new Date(
  1089.                         d.getFullYear() + 1,
  1090.                         0,
  1091.                         d.getDate()
  1092.                     );
  1093.                     return next.getTime();
  1094.                 }
  1095.                 var next = new Date(
  1096.                     d.getFullYear(),
  1097.                     d.getMonth() + 1,
  1098.                     d.getDate()
  1099.                 );
  1100.                 return next.getTime();
  1101.             },
  1102.             prevStart: function(currTime){
  1103.                 var d = new Date(currTime);
  1104.                 if(d.getMonth == 0){
  1105.                     var next = new Date(
  1106.                         d.getFullYear() - 1,
  1107.                         11,
  1108.                         d.getDate()
  1109.                     );
  1110.                     return next.getTime();
  1111.                 }
  1112.                 var next = new Date(
  1113.                     d.getFullYear(),
  1114.                     d.getMonth() - 1,
  1115.                     d.getDate()
  1116.                 );
  1117.                 return next.getTime();
  1118.             },
  1119.             getRangeLabel: function(currTime){
  1120.                 return "";
  1121.             },
  1122.             getLabel: function(currTime){
  1123.                 var d = new Date(currTime);
  1124.                 return d.toLocaleFormat("%B") + ", " + d.getFullYear();
  1125.             },
  1126.             _isLeapYear: function(currTime){
  1127.                 var now = new Date(currTime);
  1128.                 return now.getFullYear() % 4 == 0;
  1129.             },
  1130.             _daysInMonth: function(currTime){
  1131.                 var now = new Date(currTime);
  1132.                 var currMonth = now.getMonth();
  1133.                 switch(currMonth){
  1134.                     case 5:
  1135.                     case 3:
  1136.                     case 8:
  1137.                     case 10:
  1138.                         return 30;
  1139.                     case 1:
  1140.                         return this._isLeapYear() ? 29 : 28;
  1141.                     default:
  1142.                         return 31;
  1143.                 }
  1144.             },
  1145.             getStart: function(currTime){
  1146.                 var now = new Date(currTime);
  1147.                 var startOfMonth = new Date(
  1148.                     now.getFullYear(),
  1149.                     now.getMonth(),
  1150.                     1
  1151.                 );
  1152.                 return startOfMonth.getTime();
  1153.             },
  1154.             getRange: function(currTime){
  1155.                 var start = this.getStart(currTime);
  1156.                 return {
  1157.                     start: start,
  1158.                     end: start + 
  1159.                         ( this._daysInMonth(currTime) * bthistory.ONEDAY)
  1160.                 };
  1161.             }
  1162.         },
  1163.     },
  1164.     goToday: function(){
  1165.         this.currTime = Date.now();
  1166.         this.currOffset = 0;
  1167.         this.loadView(this.currView, this.currDur);
  1168.     },
  1169.     changeRange: function(dur){
  1170.         this.currOffset = 0;
  1171.         this.loadView(this.currView, dur);
  1172.     },
  1173.     goNext: function(){
  1174.         this.currTime = this._durHelper[this.currDur].nextStart(this.currTime);
  1175.         this.currOffset = 0;
  1176.         this.loadView(this.currView, this.currDur);
  1177.     },
  1178.     goPrevious: function(){
  1179.         this.currTime = this._durHelper[this.currDur].prevStart(this.currTime);
  1180.         this.currOffset = 0;
  1181.         this.loadView(this.currView, this.currDur);
  1182.     },
  1183.     loadTitle: function(view, dur){
  1184.         if(view == "results"){
  1185.             switch (this.currResultType){
  1186.                 case 'search':
  1187.                     var q = this.currQuery;
  1188.                     if(!q || !q.length){
  1189.                         q = " ";
  1190.                     }
  1191.                     document.getElementById("datetimetitle").value = 
  1192.                         this.app.getString("results.title", q);
  1193.                     break;
  1194.                 case 'more':
  1195.                     var rangelabel = this._durHelper[dur].getRangeLabel(this.currTime);
  1196.                     document.getElementById("datetimetitle").value =
  1197.                         this.app.getString(
  1198.                             "results.moretitle",
  1199.                             this.app.tracker.types[this.currMoreType].name_plural,
  1200.                             this._durHelper[dur].getLabel(this.currTime) +
  1201.                                 (rangelabel ? ", " : "") + rangelabel
  1202.                         );
  1203.                     break;
  1204.                 case 'visits':
  1205.                     document.getElementById("datetimetitle").value =
  1206.                         this.app.getString("results.visittitle",
  1207.                                 this.app.storage.IDToTitle(this.currVisitID));
  1208.                     break;
  1209.            }
  1210.         } else {
  1211.             var d = new Date(this.currTime);
  1212.             var rangelabel = this._durHelper[dur].getRangeLabel(this.currTime);
  1213.             var timelabel = this._durHelper[dur].getLabel(this.currTime);
  1214.             if(timelabel.length && rangelabel != timelabel){
  1215.                 document.getElementById("datetimetitle").value =
  1216.                     timelabel + (rangelabel ? ", " : "") + rangelabel;
  1217.             } else {
  1218.                 document.getElementById("datetimetitle").value = rangelabel;
  1219.             }
  1220.         }
  1221.     },
  1222.     resetHash: function(){
  1223.         var hash = "#view="+ this.currView + 
  1224.             "&dur="+ this.currDur + "&time=" + this.currTime;
  1225.  
  1226.         if(this.currView == "results"){
  1227.            hash += "&type=" + this.currResultType; 
  1228.            switch(this.currResultType){
  1229.                case 'search':
  1230.                    hash += "&query=" + encodeURIComponent(this.currQuery); 
  1231.                    if(document.getElementById('searchfromcb').checked){
  1232.                         var d = document.getElementById("searchfrom");
  1233.                         hash += "&starttime=" + d.dateValue.getTime();
  1234.                    }
  1235.                    if(document.getElementById('searchtocb').checked){
  1236.                         var d = document.getElementById("searchto");
  1237.                         hash += "&endtime=" + d.dateValue.getTime();
  1238.                    }
  1239.                break;
  1240.                case 'more':
  1241.                    hash += "&mtype=" + this.currMoreType;
  1242.                    break;
  1243.                case 'visits':
  1244.                    hash += "&vid=" + this.currVisitID;
  1245.                    break;
  1246.            }
  1247.  
  1248.            hash += "&order=" + this.currOrder; 
  1249.            hash += "&offset=" + this.currOffset; 
  1250.            hash += "&desc=" + (this.currOrderDesc ? "1" : "0"); 
  1251.            hash += "&types=" + this.currTypes.join(";");
  1252.         } else {
  1253.             if(this.currView == 'thumbnail'){
  1254.                 hash += "&offset=" + this.currOffset; 
  1255.             }
  1256.             hash += "&types=" + this.currTypes.join(";");
  1257.             if(this.currFilter && this.currFilter.length){
  1258.                 hash += "&filter= " + encodeURIComponent(this.currFilter);
  1259.             }
  1260.         }
  1261.         window.location.hash = hash;
  1262.         this._currHash = hash.substring(1);
  1263.     },
  1264.     loadView: function(view, dur, selectID){
  1265.         var d = new Date(this.currTime);
  1266.         this._rangetime = this.currTime;
  1267.         this._range = this._durHelper[dur].getRange(this.currTime);
  1268.         this.loadTitle(view, dur);
  1269.         this.hideBubblePreview();
  1270.  
  1271.         this.removeClass(document.getElementById("category"), "controlselected");
  1272.         this.removeClass(document.getElementById("thumbnail"), "controlselected");
  1273.         this.removeClass(document.getElementById("calendar"), "controlselected");
  1274.         this.removeClass(document.getElementById("piechart"), "controlselected");
  1275.         this.removeClass(document.getElementById("results"), "controlselected");
  1276.         
  1277.         var viewEl = document.getElementById(view);
  1278.         this.addClass(viewEl, "controlselected");
  1279.         
  1280.         document.getElementById("hour.check").style.display = "none";
  1281.         document.getElementById("day.check").style.display = "none";
  1282.         document.getElementById("week.check").style.display = "none";
  1283.         document.getElementById("month.check").style.display = "none";
  1284.         document.getElementById("hour").className = "";
  1285.         document.getElementById("day").className = "";
  1286.         document.getElementById("week").className = "";
  1287.         document.getElementById("month").className = "";
  1288.         document.getElementById(dur + ".check").style.display = "block";
  1289.         document.getElementById(dur).className = "current";
  1290.         document.getElementById("filter_box").setAttribute("hidden", "false");
  1291.         document.getElementById("searchdate_box").setAttribute("hidden", "true");
  1292.         document.getElementById("results").setAttribute("hidden", view != "results");
  1293.  
  1294.         var bodyheader = document.getElementById("body_header");
  1295.         bodyheader.setAttribute("hidden", "true");
  1296.         var len = bodyheader.childNodes.length;
  1297.         while(len--){
  1298.             bodyheader.removeChild(bodyheader.childNodes[len]);
  1299.         }
  1300.  
  1301.         this.currView = view;
  1302.         this.currDur = dur;
  1303.         this.app.log("history::"+view);
  1304.  
  1305.         var body = document.getElementById("body");
  1306.         body.setAttribute("width", "");
  1307.         body.setAttribute("style", "");
  1308.         document.getElementById("btfilter_deck").setAttribute("hidden", "false");
  1309.         document.getElementById("duration_control").setAttribute("hidden", "false");
  1310.         document.getElementById("gotoday").setAttribute("hidden", "false");
  1311.         document.getElementById("gotodate").setAttribute("hidden", "false");
  1312.         var len = body.childNodes.length;
  1313.         while(len--){
  1314.             body.removeChild(body.childNodes[len]);
  1315.         }
  1316.         this.currTypes = this.buildTypes();
  1317.         this.currFilter = document.getElementById("filter_text").value;
  1318.         
  1319.         this.resetHash();
  1320.         this._data = this.controllers[view].queryTracker(this.currTypes, this.currFilter);
  1321.  
  1322.         this.controllers[view].loadView(selectID);
  1323.     },
  1324.     handleMoreItems: function(type){
  1325.         this.app.log("history::handleMoreItems (" + type + ")");
  1326.  
  1327.         this.currOrder = 'starttime';
  1328.         this.currOrderDesc = true;
  1329.         this.currOffset = 0;
  1330.         this.currResultType = "more";
  1331.         this.currMoreType = type;
  1332.         this.loadView("results", this.currDur);
  1333.  
  1334.     },
  1335.     datestring: function(d){
  1336.         var label = bthistory._dayLabel(d);
  1337.         var date = new Date(d);
  1338.         return label + ", " + date.toLocaleTimeString();
  1339.     },
  1340.     duration: function(d){
  1341.         if(d <= 0){
  1342.             return " ";
  1343.         }
  1344.         var days = 0;
  1345.         var hours = 0;
  1346.         var mins = 0;
  1347.         var secs = 0;
  1348.         if(d > this.ONEDAY){
  1349.             days = Math.floor(d / this.ONEDAY);
  1350.             d = d % this.ONEDAY;
  1351.         }
  1352.         if(d > this.ONEHOUR){
  1353.             hours = Math.floor(d / this.ONEHOUR);
  1354.             d = d % this.ONEHOUR;
  1355.         }
  1356.  
  1357.         if(d > this.ONEMINUTE){
  1358.             mins = Math.floor(d /this.ONEMINUTE);
  1359.             d = d % this.ONEMINUTE;
  1360.         }
  1361.         secs = Math.floor(d / 1000);
  1362.         if(days > 0){
  1363.             return this.app.getString("history.duration.days", days, hours, mins, secs);
  1364.         } else if (hours > 0){
  1365.             return this.app.getString("history.duration.hours", hours, mins, secs);
  1366.         } else if (mins > 0){
  1367.             return this.app.getString("history.duration.min", mins, secs);
  1368.         }
  1369.         if(!secs){
  1370.             return " ";
  1371.         }
  1372.         return this.app.getString("history.duration.secs", secs);
  1373.  
  1374.     },
  1375.     doCreateCategory: function() {
  1376.         window.openDialog("chrome://boomtango/content/createCategory.xul", "",
  1377.             "chrome, dialog, modal").focus();
  1378.  
  1379.     },
  1380.     doHotkeys: function(){
  1381.         window.openDialog('chrome://boomtango/content/hotkeys.xul', 'hotkeysDlg', 'dialog=yes,resize=no,toolbar,centerscreen').focus();
  1382.     },
  1383.     doSettings: function(){
  1384.         window.openDialog('chrome://boomtango/content/settings.xul', 'settingsDlg', 'dialog=yes,resize=no,toolbar,centerscreen').focus();
  1385.     }
  1386. };
  1387.